#TODO: extract AbstractDeserializer and move this into valid::Deserializer
#TODO: protected function list 

from bcat.gtp.serde.dev2.impl.bytes_reader import BytesReader
from bcat.gtp.serde.dev2.impl.special_char import SpecialChar

from bcat.gtp.serde.error import NeedAlphaError, NeedDigitError, NeedOpenBracesError, NeedCommaError, NeedEqualSignError, NeedCloseBracesError, UnexpectEndOfStreamError, NormalByteAfterEscapeError, InvalidSpecialByteError, NeedOpenBracketsError, NeedCloseBracketsError
from bcat.gtp.serde.dev2.valid.impl.map_access import MapAccess
from bcat.gtp.serde.dev2.valid.impl.seq_access import SeqAccess

    
from bcat.gtp.serde.dev2.ideserializer import IDeserializer
class Deserializer(IDeserializer):
    @staticmethod
    def from_str(s, codec="utf-8"):
        return Deserializer.from_bytes(s.encode(codec))
    @staticmethod
    def from_bytes(in_bytes):
        return Deserializer.from_reader(BytesReader(in_bytes))


    @staticmethod
    def from_reader(reader):
        return Deserializer(reader)

    def __init__(self, reader):
        self.__reader = reader

    def reader(self):
        return self.__reader

    def deserialize_key(self, visitor):
        result = bytearray()
        b = self.reader().next_or_raise()
        if SpecialChar.is_alpha(b):
            result.append(b)
            b = self.reader().next_or_raise()
            if SpecialChar.is_digit(b):
                result.append(b)     
                b = self.reader().next_or_raise()
                if SpecialChar.is_digit(b):
                    result.append(b)
                    return visitor.visit_key(result)
                else:
                    raise NeedDigitError(self.reader().position())
            else:
                raise NeedDigitError(self.reader().position())
        else:
            raise NeedAlphaError(self.reader().position())
        return False

    #escape char -> meet single escape bytes
    def _parse_bytes(self):
        result = bytearray()
        while True:
            #EOS  -> byte finished
            # \ -> enter escape
            # {, [  -> raise Exception 
            #, =  ] } -> byte finished
            # else continue 
            peek = self.reader().peek()
            if peek is not None: # not-EOS
                # not ,= { [ ] }
                if SpecialChar.is_byte(peek):
                    result.append(peek)
                    self.reader().eat()
                # \ -> enter escape
                elif SpecialChar.is_slash(peek):
                    self.reader().eat()

                    peek = self.reader().peek()
                    if peek is not None:
                        if SpecialChar.is_escape(peek):
                            result.append(peek)
                            self.reader().eat()
                        else:
                            msg = "meet invalid-escape-char:[{}:{}] for pasing bytes".format(chr(peek) if peek < 0x8f else peek, peek)
                            raise NormalByteAfterEscapeError(self.reader().position(), msg)
                    else:
                        msg = "end after slash, (\\?end?) for parsing bytes"
                        raise UnexpectEndOfStreamError(self.reader().position(), msg)
                # {[ ->  raise Exception 
                elif SpecialChar.is_open_char(peek):
                    msg = "meet invalid special char[{}:{}] for parsing bytes, bytes cann't contains {{ or [ ".format(chr(peek) if peek < 0x8f else peek, peek)
                    raise InvalidSpecialByteError(self.reader().position(), msg)
                # ,=]} -> bytes finished
                else: 
                    return result
            else: #end of bytes
                return result


    def deserialize_bytes(self, visitor):
        return visitor.visit_bytes(self._parse_bytes())


    def deserialize_list(self, visitor):
        self._deser_list_begin()
        
        result = visitor.visit_list(SeqAccess(self))
        if result is not None:
            self._deser_list_end()
        return result

    def deserialize_dict(self, visitor):
        self._deser_dict_begin() 
        
        result = visitor.visit_dict(MapAccess(self))
        if result is not None:
            self._deser_dict_end()
        return result


    def deserialize_value(self, visitor):
        peek = self.reader().peek()
        if peek is not None:
            if SpecialChar.is_object_begin(peek):
                return self.deserialize_dict(visitor)
            elif SpecialChar.is_array_begin(peek):
                return self.deserialize_list(visitor)
            else:
                return self.deserialize_bytes(visitor)
        else:
            raise UnexpectEndOfStreamError(self.reader().position())

    def is_end(self):
        return self.reader().available() == 0






    def _deser_list_begin(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_array_begin(b):
            self.reader().eat()
        else:
            raise NeedOpenBracketsError(self.reader().position())
    def _deser_list_end(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_array_end(b):
            self.reader().eat()
        else:
            raise NeedCloseBracketsError(self.reader().position())
        
    def _deser_dict_begin(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_object_begin(b):
            self.reader().eat()
        else:
            raise NeedOpenBracesError(self.reader().position())

    def _deser_dict_end(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_object_end(b):
            self.reader().eat()
        else:
            raise NeedCloseBracesError(self.reader().position())

    def deser_kv_separator(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_kv_separator(b):
            self.reader().eat()
        else:
            raise NeedEqualSignError(self.reader().position())
        
    def _deser_pair_separator(self):
        b = self.reader().peek_or_raise()
        if SpecialChar.is_pair_separator(b):
            self.reader().eat()
        else:
            raise NeedCommaError(self.reader().position())
        








from bcat.gtp.serde.dev2.valid.impl.map_access import MapAccess
from bcat.gtp.serde.dev2.valid.impl.validates import ValidateKey, ValidateValue
from bcat.gtp.serde.error import PairEndError, InvalidSpecialByteError


import unittest
class TestDeserializer(unittest.TestCase):
    class MockVisitor(object):
        def visit_key(self, v):
            return v  
        def visit_dict(self, imap_access):
            while  imap_access.has_next_entry():
                (k, v) = imap_access.next_entry(ValidateKey, ValidateValue)
                if not k or not v:
                    return False
            return True
        def visit_bytes(self, v):
            return v
        def visit_list(self, iseq_access):
            while iseq_access.has_next_element():
                e = iseq_access.next_element(ValidateValue)
                if e == False:
                    return False
            return True
            

    def setUp(self):
        self.visitor = TestDeserializer.MockVisitor()

    def test_key(self):
        der = Deserializer.from_str('X01')
        self.assertEqual(b'X01', der.deserialize_key(self.visitor))
    
    def test_bytes(self):
        def expect(ctx, expect, in_bytes):  
            der = Deserializer.from_bytes(in_bytes)
            ctx.assertEqual(expect, der.deserialize_bytes(ctx.visitor))
        s = b'XXXXXXXXX010101010'
        expect(self, s, s)
        expect(self, b'\\', b'\\\\')
        expect(self, b',', b'\\,')
        expect(self, b'=', b'\\=')
        expect(self, b'[', b'\\[')
        expect(self, b']', b'\\]')
        expect(self, b'{', b'\\{')
        expect(self, b'}', b'\\}')
        expect(self, b'}}', b'\\}\\}')

    def test_bytes_exception(self):
        der=Deserializer.from_bytes(b'\\a')
        self.assertRaisesRegex(NormalByteAfterEscapeError, "97", der.deserialize_bytes, self.visitor)

        der=Deserializer.from_bytes(b'\\')
        self.assertRaisesRegex(UnexpectEndOfStreamError, "end after slash", der.deserialize_bytes, self.visitor)
            
        der=Deserializer.from_bytes(b'aaaaa,')
        self.assertEqual(b"aaaaa", der.deserialize_bytes(self.visitor))

        der=Deserializer.from_bytes(b'aaaaa=')
        self.assertEqual(b"aaaaa", der.deserialize_bytes(self.visitor))

        der=Deserializer.from_bytes(b'aaaaa]')
        self.assertEqual(b"aaaaa", der.deserialize_bytes(self.visitor))

        der=Deserializer.from_bytes(b'aaaaa}')
        self.assertEqual(b"aaaaa", der.deserialize_bytes(self.visitor))

        der=Deserializer.from_bytes(b'aaaaa{')
        self.assertRaisesRegex(InvalidSpecialByteError, "123", der.deserialize_bytes, self.visitor)
        der=Deserializer.from_bytes(b'aaaaa[')
        self.assertRaisesRegex(InvalidSpecialByteError, "91", der.deserialize_bytes, self.visitor)
        

    def test_dict(self):
        der = Deserializer.from_str('{X01=X02}')
        self.assertTrue(der.deserialize_dict(self.visitor))
        self.assertTrue(der.is_end())

    def test_dict_of_bytes(self):
        der = Deserializer.from_str('{X01=XXXXXXX}')
        self.assertTrue(der.deserialize_dict(self.visitor))

        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344}')
        self.assertTrue(der.deserialize_dict(self.visitor))

        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344,}')
        self.assertRaisesRegex(NeedAlphaError, "alpha", der.deserialize_dict, self.visitor)

        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344[}') # parse bytes error
        self.assertRaisesRegex(InvalidSpecialByteError, "91", der.deserialize_dict, self.visitor)
        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344{}')
        self.assertRaisesRegex(InvalidSpecialByteError, "123", der.deserialize_dict, self.visitor)

        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344=}') # has_next_entry error
        self.assertRaisesRegex(PairEndError, "=", der.deserialize_dict, self.visitor)

        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344]}') # has_next_entry error
        self.assertRaisesRegex(PairEndError, "]", der.deserialize_dict, self.visitor)


        der = Deserializer.from_str('{X01=XXXXXXX,X02=AAAAAA,X03=344},,,}')
        der.deserialize_dict(self.visitor)
        self.assertTrue(not der.is_end())

    
    def test_str(self):
        s = 'XXXXXXXXX010101010'
        der = Deserializer.from_str(s)
        self.assertEqual(s.encode("utf-8"), der.deserialize_bytes(self.visitor))

    def test_value(self):
        data =  b'abdcdee'
        der = Deserializer.from_bytes(data)
        self.assertEqual(data, der.deserialize_value(self.visitor))

        data = b'{X03=03333}'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_value(self.visitor))
    
    def test_value_with_dict(self):
        data = b'{X01={X02=XXXXXXX}}'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_dict(self.visitor))

        data = b'{X01={X02={X03={X05={X06=XXXXXXX}},X04=AAAAA}}}'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_dict(self.visitor))

        data = b'{X01=[11,22]}'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_dict(self.visitor))


    def test_value_with_list(self):
        data = b'[AAAA]'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_list(self.visitor))

        data = b'[AAAA,BBB,CCC,DD,{X01=11111}]'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_list(self.visitor))

        
        data = b'[AAAA,BBB,CCC,{X01=[DD,EE]},[MM,NN]]'
        der = Deserializer.from_bytes(data)
        self.assertTrue(der.deserialize_list(self.visitor))


